package assist.datastore.dao;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Set;
import java.util.TreeSet;

import javax.jdo.PersistenceManager;
import javax.jdo.Query;

import java.util.logging.Logger;

import assist.datastore.Datastore;
import assist.datastore.JDODatastore;
import assist.domain.model.Event;
import assist.domain.model.EventPlayerOnIce;
import assist.domain.model.EventQuery;
import assist.domain.model.Game;
import assist.domain.model.Player;
import assist.domain.model.StatsQuery.Strength;
import assist.domain.model.Team;

public abstract class EventDAO<T extends Event>
    extends EntityDAO<T> implements EventFinder<T>
{
    private static Logger logger = 
        Logger.getLogger(EventDAO.class.getName());   
    protected EventDAO(Datastore<T> datastore, Class<T> domainClass)
    {
        super(datastore, domainClass);
    }

    
    public Double getEventCount(Team team, Set<Long> players, Set<String> gameIds, Strength strength, boolean against)
    {
        logger.fine("Getting Event Count");
        PersistenceManager pm = JDODatastore.getPersistenceManager();
        //See defect #60.
        if (against)
        {
            if (strength == Strength.PP)
            {
                strength = Strength.SH;
            }
            else if (strength == Strength.SH)
            {
                strength = Strength.PP;
            }            
        }
        try
        {           
            Query query = pm.newQuery(getQueryJDOQL(players, against));
            return new Double((Long)query.execute(team.getAbbreviation(), 
                                    strength.toString(), new ArrayList<String>(gameIds)));
        }
        finally
        {
            pm.close();
        }               
    }
    
    private String getQueryJDOQL(Set<Long> players, boolean against)
    {                
        String epoiClass = EventPlayerOnIce.class.getName();
        String eventClass =  this.getDomainClass().getName();
        String queryJDOQL = "SELECT count (distinct this.key) FROM " 
            + eventClass
            + " where "
            + ((against)?"!":"")
            + "this.teamId.equals(:teamIdParam)"
            + " && this.strength.equals(:strengthParam)"
            + " && "
            + " :gameIdsParam.contains(this.gameId)";
           

            for (Long player : players)
            {
                queryJDOQL += " && (SELECT eventId FROM " 
                    + epoiClass + " WHERE playerId == "
                    + player 
                    + ").contains(this.key) ";               
            }
            /*
            int i = 0;
            if (evtQuery.getOpponents() != null && !evtQuery.getOpponents().isEmpty())
            {
                queryJDOQL += " && (SELECT eventId FROM " + epoiClass + " WHERE ";
                for (Player oppPlayer : evtQuery.getOpponents())
                {
                    queryJDOQL += " playerId == " + oppPlayer.getPlayerId();
                    if (++i < evtQuery.getOpponents().size())
                    {
                        queryJDOQL += " || ";
                    }
                }
                queryJDOQL += ").contains(this.key) ";
            }*/
        logger.fine(queryJDOQL);                
        return queryJDOQL;
    }    
    
    @Override
    public Collection<T> findByGameId(String gameId)
    {
        //TODO This should use the EntityDAO.findByCriteria, which then
        //uses the datastore.findByCriteria. 
        return null;
    }    

    public Long getEventCount(EventQuery evtQuery, boolean against)
    {
        return this.getEventCount(evtQuery, against, new TreeSet<Player> ());
    }
 
    public Long getEventCount(final EventQuery evtQuery, final boolean against,
            Player player)
    { 
        Set <Player> players = new TreeSet<Player> ();
        players.add(player);
        return this.getEventCount(evtQuery, against, players);
    }    

    
    
    public Long getEventCount(final EventQuery evtQuery, final boolean against, 
            final Set<Player> players)
    {
        logger.fine("Get Event Count.");
        PersistenceManager pm = JDODatastore.getPersistenceManager();
        try
        {           
            Query query = pm.newQuery(getQueryJDOQL(evtQuery, against, players));
            Collection<String> gameIds = new ArrayList<String>();
            for(Game game: evtQuery.getGames())
            {
                gameIds.add(game.getId());
            }
            return (Long) query.execute(evtQuery.getTeam().getAbbreviation(), gameIds);
        }
        finally
        {
            pm.close();
        }                      
    }
    
    private String getQueryJDOQL(EventQuery evtQuery, boolean against,
            Set<Player> players)
    {                
        String epoiClass = EventPlayerOnIce.class.getName();
        String eventClass =  this.getDomainClass().getName();
        String queryJDOQL = "SELECT count (distinct this.key) FROM " 
            + eventClass
            + " where "
            + ((against)?"!":"")
            + "this.teamId.equals(:teamIdParam)"
            + " && ";
            String strength ="(";
            boolean first = true;
            if (evtQuery.getTypes().getEvenStrength())
            {
                first = false;
                strength += " this.strength.equals(\"EV\")";
            }
            if (evtQuery.getTypes().getShortHanded())
            {
                if (!first) 
                {
                    first = false;
                    strength += " || ";
                }  
                strength += " this.strength.equals(\"SH\") ";
            }
            if (evtQuery.getTypes().getPowerPlay())
            {
                if (!first)
                {
                    first = false;
                    strength += " || ";
                }
                strength += " this.strength.equals(\"PP\") ";
            }            
            strength += ") ";
            queryJDOQL += strength
            + " && "
            + " :gameIdsParam.contains(this.gameId)";
           

            for (Player player : players)
            {
                queryJDOQL += " && (SELECT eventId FROM " 
                    + epoiClass + " WHERE playerId == "
                    + player.getPlayerId() 
                    + ").contains(this.key) ";
               
            }
            int i = 0;
            if (evtQuery.getOpponents() != null && !evtQuery.getOpponents().isEmpty())
            {
                queryJDOQL += " && (SELECT eventId FROM " + epoiClass + " WHERE ";
                for (Player oppPlayer : evtQuery.getOpponents())
                {
                    queryJDOQL += " playerId == " + oppPlayer.getPlayerId();
                    if (++i < evtQuery.getOpponents().size())
                    {
                        queryJDOQL += " || ";
                    }
                }
                queryJDOQL += ").contains(this.key) ";
            }
        logger.fine(queryJDOQL);                
        return queryJDOQL;
    }

}
